/*******************************************************************

    Author: Xiao
    Date: 2022/3/30 9:39

*******************************************************************/
package utils

import (
	"bufio"
	"crypto/md5"
	"fmt"
	"github.com/nfnt/resize"
	"image"
	"image/gif"
	"image/jpeg"
	"image/png"
	"io"
	"mcr/pkg/config"
	"mcr/pkg/logs"
	"mime/multipart"
	"os"
	"strings"
)

// 图像类型
const (
	PNG  = "png"
	JPG  = "jpg"
	JPEG = "jpeg"
	GIF  = "gif"
)

var (
	imgType = []string{PNG, JPG, JPEG, GIF}
)

type FileMsg struct {
	FilePath  string
	FileThumb string
	FileType  string
	FileSha   string
	FileSize  int64
}

// 保存图片
func SaveImage(head bool, f multipart.File) (FileMsg, error) {
	var a FileMsg
	buf := bufio.NewReader(f)
	// 进行图片的解码
	img, iType, err := image.Decode(buf)
	if err != nil {
		logs.Log.Errorf("解析图片文件出错:%s", err)
		return a, err
	}
	// 判断图片类型是否合法
	if !isImgType(iType) {
		logs.Log.Errorf("图片格式不合法:%s", iType)
		return a, fmt.Errorf("图片格式不合法")
	}
	if head {
		img = resize.Resize(400, 400, img, resize.Lanczos3)
	}
	// 初始化 MD5 实例
	md5Hash := md5.New()
	// 读入缓存
	_, err = f.Seek(0, 0)
	if err != nil {
		logs.Log.Errorf("读入缓存失败:%s", err)
		return a, err
	}
	newBuf := bufio.NewReader(f)
	_, err = io.Copy(md5Hash, newBuf)
	if err != nil {
		logs.Log.Errorf("读入MD5失败:%s", err)
		return a, err
	}

	// 进行md5编码，16进制的 byte 数组
	fileMd5FX := md5Hash.Sum(nil)
	fileMd5 := fmt.Sprintf("%x", fileMd5FX)

	// 组合文件完整路径
	// 目录
	dirPath := setDirPath("thumb/image/")
	savePath := "thumb/image/"
	if head {
		savePath += "head/"
		dirPath += "head/"
	}
	// 文件路径
	filePath := dirPath + fileMd5
	savePath += fileMd5
	if head {
		a.FileType = "png"
		filePath += ".png"
		savePath += ".png"
	} else {
		a.FileType = iType
		filePath += "." + iType
		savePath += "." + iType
	}
	// 获取目录信息，并创建目录
	dirInfo, err := os.Stat(dirPath)
	if err != nil {
		err = os.MkdirAll(dirPath, 0755)
		if err != nil {
			logs.Log.Errorf("创建图片文件目录失败:%s", err)
			return a, err
		}
	} else {
		if !dirInfo.IsDir() {
			err = os.MkdirAll(dirPath, 0755)
			if err != nil {
				logs.Log.Errorf("创建图片文件目录失败:%s", err)
				return a, err
			}
		}
	}
	// 存图片文件
	fileInfo, err := os.Stat(filePath)
	if err != nil {
		// 打开一个文件,文件不存在就会创建
		file, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY, 0755)
		if err != nil {
			logs.Log.Errorf("打开图片文件失败:%s", err)
			return a, err
		}
		defer file.Close()
		if iType == PNG {
			err = png.Encode(file, img)
		} else if iType == JPG || iType == JPEG {
			err = jpeg.Encode(file, img, nil)
		} else if iType == GIF {
			// 重新对 gif 格式进行解码
			// image.Decode 只能读取 gif 的第一帧
			// 设置下次读写位置（移动文件指针位置）
			if head {
				_, err = f.Seek(0, 0)
				if err != nil {
					logs.Log.Errorf("Gif读取针错误:%s", err)
					return a, err
				}
				gifImg, err := gif.DecodeAll(f)
				if err != nil {
					logs.Log.Errorf("Gif编码错误:%s", err)
					return a, err
				}
				err = gif.EncodeAll(file, gifImg)
			} else {
				err = gif.Encode(file, img, nil)
			}

		}
		if err != nil {
			logs.Log.Errorf("编码图片失败:%s", err)
			return a, err
		}
		fileState, err := file.Stat()
		if err != nil {
			logs.Log.Errorf("获取图片文件信息失败:%s", err)
		}
		a.FileSize = fileState.Size()
	} else {
		a.FileSize = fileInfo.Size()
	}
	a.FilePath = savePath
	a.FileThumb = savePath
	a.FileSha = fileMd5
	return a, nil
}

// 判断是否有定义中的图像格式
func isImgType(str string) bool {
	// 转小写
	str = strings.ToLower(str)
	for _, v := range imgType {
		if v == str {
			return true
		}
	}
	return false
}

//生成图片存储的目录（store/md5Str/...）
func setDirPath(md5Str string) string {
	var str = strings.Builder{}
	str.WriteString(config.ConfigValue.StorePath)
	str.WriteString(md5Str)
	return str.String()
}

// 保存图片
func SaveMusic(f multipart.File) (FileMsg, error) {
	var a FileMsg

	// 初始化 MD5 实例
	md5Hash := md5.New()
	// 读入缓存
	_, err := f.Seek(0, 0)
	if err != nil {
		logs.Log.Errorf("重置文件失败:%s", err)
		return a, err
	}
	newBuf := bufio.NewReader(f)
	_, err = io.Copy(md5Hash, newBuf)
	if err != nil {
		logs.Log.Errorf("读入MD5失败:%s", err)
		return a, err
	}
	// 进行md5编码，16进制的 byte 数组
	fileMd5FX := md5Hash.Sum(nil)
	fileMd5 := fmt.Sprintf("%x", fileMd5FX)
	// 组合文件完整路径
	// 目录
	dirPath := setDirPath("thumb/music/")
	savePath := "thumb/music/" + fileMd5 + ".mp3"

	// 文件路径
	filePath := dirPath + fileMd5 + ".mp3"
	// 获取目录信息，并创建目录
	dirInfo, err := os.Stat(dirPath)
	if err != nil {
		err = os.MkdirAll(dirPath, 0755)
		if err != nil {
			logs.Log.Errorf("创建音乐文件目录失败:%s", err)
			return a, err
		}
	} else {
		if !dirInfo.IsDir() {
			err = os.MkdirAll(dirPath, 0755)
			if err != nil {
				logs.Log.Errorf("创建音乐文件目录失败:%s", err)
				return a, err
			}
		}
	}
	// 存文件
	fileInfo, err := os.Stat(filePath)
	if err != nil {
		// 打开一个文件,文件不存在就会创建
		file, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY, 0755)
		if err != nil {
			logs.Log.Errorf("打开文件失败:%s", err)
			return a, err
		}
		defer file.Close()
		// 读入缓存
		_, err = f.Seek(0, 0)
		if err != nil {
			logs.Log.Errorf("重置文件失败:%s", err)
			return a, err
		}
		_, err = io.Copy(file, f)
		if err != nil {
			logs.Log.Errorf("保存失败:%s", err)
			return a, err
		}
		fileState, err := file.Stat()
		if err != nil {
			logs.Log.Errorf("获取文件信息失败:%s", err)
		}
		a.FileSize = fileState.Size()
	} else {
		a.FileSize = fileInfo.Size()
	}
	a.FileType = "mp3"
	a.FilePath = savePath
	a.FileThumb = savePath
	a.FileSha = fileMd5
	return a, nil
}
